﻿using System.Linq.Expressions;
using AutoMapper;
using FlyingHat.Common.Extensions;
using FlyingHat.Common.Helper;
using FlyingHat.Core.Entities;
using FlyingHat.Model.Enums;
using FlyingHat.Model.Models;
using FlyingHat.Model.Models.Global;
using FlyingHat.Office.Import;
using FlyingHat.Repository.IRepository;
using FlyingHat.Service.IService;

namespace FlyingHat.Service.Service
{
    public class UserService : IUserService
    {
        private readonly IUserRepository userRepository;
        private readonly IUserLogRepository userLogRepository;
        private readonly IMapper mapper;
        public UserService(IUserRepository userRepository, IMapper mapper, IUserLogRepository userLogRepository)
        {
            this.userRepository = userRepository;
            this.mapper = mapper;
            this.userLogRepository = userLogRepository;
        }

        public async Task<UserModel?> Login(string userName, string password)
        {
            var user = await userRepository.FirstOrDefaultAsync(i => i.UserName == userName && !i.IsDeleted).ConfigureAwait(false);
            // 有关用户名和密码的问题，一律返回 “用户名或密码错误”，这样可以模糊错误原因
            if (user == null)
            {
                return null;
            }
            if (user.Password != EncryptHelper.GetPassword(password, user.Salt))
            {
                return null;
            }
            var umodel = mapper.Map<UserModel>(user);
            return umodel;
        }

        public async Task<IReturnModel> StudentList(string? userName, UserStatus? userStatus, int page, int limit)
        {
            Expression<Func<User, bool>> where = i => i.Identity == Identity.Student && !i.IsDeleted;
            if (userStatus.HasValue)
            {
                where = where.And(i => i.UserStatus == userStatus);
            }
            if (!string.IsNullOrWhiteSpace(userName))
            {
                where = where.And(i => i.UserName.Contains(userName));
            }
            var res = await this.userRepository.GetListAsync<DateTime, UserModel>(where, i => i.LastLoginTime, page, limit);
            return res;
        }

        public async Task<IReturnModel> AddOrUpdateStudent(UserModel user)
        {
            // 检查手机号是否重复
            var exist = await userRepository.ExistAsync(i => i.Id != user.Id && i.Phone == user.Phone);
            if (exist)
            {
                return ReturnModel.Fail("手机号重复");
            }
            // 用户名是否重复
            exist = await userRepository.ExistAsync(i => i.Id != user.Id && i.UserName == user.UserName);
            if (exist)
            {
                return ReturnModel.Fail("用户名重复");
            }
            if (user.Id > 0)
            {
                var u = mapper.Map<User>(user);
                var flag = await userRepository.UpdateAsync(u, i => new { i.Phone, i.Gender, i.RealName }).ConfigureAwait(false);
                return ReturnModel.Result(flag, "修改");
            }
            else
            {
                var u = mapper.Map<User>(user);
                u.LastLoginTime = u.CreatedTime;
                u.Identity = Identity.Student;
                u.UserStatus = UserStatus.Init;
                u.Salt = EncryptHelper.GetSalt();
                u.Password = EncryptHelper.GetPassword($"123456", u.Salt);
                var row = await userRepository.AddAsync(u).ConfigureAwait(false);
                return ReturnModel.Result(row > 0, "新增");
            }
        }

        public async Task<IReturnModel> ModifyPasword(List<long> ids, string password)
        {
            var users = new List<User>();
            foreach (var id in ids)
            {
                var u = new User
                {
                    Id = id,
                    Salt = EncryptHelper.GetSalt()
                };
                u.Password = EncryptHelper.GetPassword(password, u.Salt);
                users.Add(u);
            }
            var flag = await userRepository.UpdateAsync(users, i => new { i.Salt, i.Password }).ConfigureAwait(false);
            return ReturnModel.Result(flag, "修改");

        }

        public async Task<IReturnModel> BatchRemoveStudent(List<long> ids)
        {
            var users = new List<User>();
            foreach (var id in ids)
            {
                var u = new User
                {
                    Id = id,
                    IsDeleted = true
                };
                users.Add(u);
            }
            var flag = await userRepository.UpdateAsync(users, i => new { i.IsDeleted }).ConfigureAwait(false);
            return ReturnModel.Result(flag, "删除");
        }

        public async Task<IReturnModel> SetStatus(string? reason, int? blockDay, UserStatus setUserStatus, List<long> ids)
        {
            var users = new List<User>();
            var userLog = new List<UserLog>();
            foreach (var id in ids)
            {
                var u = new User
                {
                    Id = id,
                    UserStatus = setUserStatus,
                    BlockDay = blockDay ?? 0
                };
                users.Add(u);
                userLog.Add(new UserLog
                {
                    UserId = id,
                    UserStatus = setUserStatus,
                    Reason = reason ?? "",
                    BlockDay = blockDay ?? 0
                });
            }
            using var tran = await userRepository.BeginTransactionAsync().ConfigureAwait(false);
            try
            {
                var flag = await userRepository.UpdateAsync(users, i => new { i.UserStatus, i.BlockDay }).ConfigureAwait(false);
                flag = (await userLogRepository.AddRangeAsync(userLog).ConfigureAwait(false)) > 0;
                await userRepository.CommitTransactionAsync().ConfigureAwait(false);
                return ReturnModel.Result(flag, "设置");
            }
            catch (Exception)
            {
                await userRepository.RollbackTransactionAsync().ConfigureAwait(false);
                throw;
            }

        }

        public async Task<IReturnModel> ImportStudent(List<Student> students)
        {
            var user = mapper.Map<List<User>>(students);
            foreach (var item in user)
            {
                item.Identity = Identity.Student;
                item.LastLoginTime = DateTime.Now;
                item.UserStatus = UserStatus.Init;
                item.Salt = EncryptHelper.GetSalt();
                item.Password = EncryptHelper.GetPassword("123456", item.Salt);
            }
            // 在此处可能存在数据库已存在的数据，因此我们需要想办法来处理它。
            // 但请注意要根据实际场景去做。
            // 1.假如您已知道数据不会超过1万，那完全可以直接从数据库读出判重
            // 2.insert ignore into,插入的时候使用该语句执行，重复或语句错误报错都会被忽略（根据主键和唯一索引判断重复）,
            // 主键非程序员管理的最好不要设置唯一索引处理
            // 3.在数据表中设置联合唯一索引，使用2或者不管它，让它报错，我们处理错误即可
            // 4.如果数据实在很多，那么可以借助第三方，例如redis,我们可以把数据先存放在redis,然后后台任务去慢慢执行保存到数据库
            // ↑↑↑↑↑↑以上实际可以联合使用
            // 对于本项目而言，学号+手机号是唯一的，所以我们需要将学号和手机号作为联合唯一索引
            var row = await userRepository.AddRangeAsync(user).ConfigureAwait(false);
            return ReturnModel.Result(row > 0, "导入");
        }
    }
}
